<!DOCTYPE html>
<html CN>







<head>
	
	
	<link rel="stylesheet" href="/css/allinone.min.css"> 

	
	<!-- Global Site Tag (gtag.js) - Google Analytics -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-42863699-1"></script>
	<script>
		window.dataLayer = window.dataLayer || [];
		function gtag(){dataLayer.push(arguments);}
		gtag('js', new Date());
		gtag('config', 'UA-42863699-1');
	</script>
	

	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />

	<title>go http 服务器编程 | Cizixs Write Here</title>

	<meta name="HandheldFriendly" content="True" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
	<meta name="generator" content="hexo">
	<meta name="author" content="Cizixs Wu">
	<meta name="description" content="">

	
	<meta name="keywords" content="">
	

	
	<link rel="shortcut icon" href="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg">
	

	
	<meta name="theme-color" content="#3c484e">
	<meta name="msapplication-TileColor" content="#3c484e">
	

	

	

	<meta property="og:site_name" content="Cizixs Write Here">
	<meta property="og:type" content="article">
	<meta property="og:title" content="go http 服务器编程 | Cizixs Write Here">
	<meta property="og:description" content="">
	<meta property="og:url" content="http://cizixs.com/2016/08/17/golang-http-server-side/">

	
	<meta property="article:published_time" content="2016-08-17T00:08:00+08:00"/> 
	<meta property="article:author" content="Cizixs Wu">
	<meta property="article:published_first" content="Cizixs Write Here, /2016/08/17/golang-http-server-side/" />
	

	
	
	<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
	

	
	<script src="https://cdn.staticfile.org/highlight.js/9.10.0/highlight.min.js"></script>
	

	
	
<link rel="stylesheet" href="/css/prism-base16-ateliersulphurpool.light.css" type="text/css"></head>
<body class="post-template">
    <div class="site-wrapper">
        




<header class="site-header outer" style="z-index: 999">
    <div class="inner">
        
<nav class="site-nav"> 
    <div class="site-nav-left">
        <ul class="nav">
            <li>
                
                <a href="/" title="Home">Home</a>
                
            </li>
            
            
            <li>
                <a href="/about" title="About">About</a>
            </li>
            
            <li>
                <a href="/archives" title="Archives">Archives</a>
            </li>
            
            
        </ul> 
    </div>
    <div class="site-nav-right">
        
<div class="social-links" >
    
    <a class="social-link" title="weibo" href="https://weibo.com/1921727853" target="_blank" rel="noopener">
        <svg viewBox="0 0 1141 1024" xmlns="http://www.w3.org/2000/svg"><path d="M916.48 518.144q27.648 21.504 38.912 51.712t9.216 62.976-14.336 65.536-31.744 59.392q-34.816 48.128-78.848 81.92t-91.136 56.32-94.72 35.328-89.6 18.944-75.264 7.68-51.712 1.536-49.152-2.56-68.096-10.24-78.336-21.504-79.872-36.352-74.24-55.296-59.904-78.848q-16.384-29.696-22.016-63.488t-5.632-86.016q0-22.528 7.68-51.2t27.136-63.488 53.248-75.776 86.016-90.112q51.2-48.128 105.984-85.504t117.248-57.856q28.672-10.24 63.488-11.264t57.344 11.264q10.24 11.264 19.456 23.04t12.288 29.184q3.072 14.336 0.512 27.648t-5.632 26.624-5.12 25.6 2.048 22.528q17.408 2.048 33.792-1.536t31.744-9.216 31.232-11.776 33.28-9.216q27.648-5.12 54.784-4.608t49.152 7.68 36.352 22.016 17.408 38.4q2.048 14.336-2.048 26.624t-8.704 23.04-7.168 22.016 1.536 23.552q3.072 7.168 14.848 13.312t27.136 12.288 32.256 13.312 29.184 16.384zM658.432 836.608q26.624-16.384 53.76-45.056t44.032-64 18.944-75.776-20.48-81.408q-19.456-33.792-47.616-57.344t-62.976-37.376-74.24-19.968-80.384-6.144q-78.848 0-139.776 16.384t-105.472 43.008-72.192 60.416-38.912 68.608q-11.264 33.792-6.656 67.072t20.992 62.976 42.496 53.248 57.856 37.888q58.368 25.6 119.296 32.256t116.224 0.512 100.864-21.504 74.24-33.792zM524.288 513.024q20.48 8.192 38.912 18.432t32.768 27.648q10.24 12.288 17.92 30.72t10.752 39.424 1.536 42.496-9.728 38.912q-8.192 18.432-19.968 37.376t-28.672 35.328-40.448 29.184-57.344 18.944q-61.44 11.264-117.76-11.264t-88.064-74.752q-12.288-39.936-13.312-70.656t16.384-66.56q13.312-27.648 40.448-51.712t62.464-38.912 75.264-17.408 78.848 12.8zM361.472 764.928q37.888 3.072 57.856-18.432t21.504-48.128-15.36-47.616-52.736-16.896q-27.648 3.072-43.008 23.552t-17.408 43.52 9.728 42.496 39.424 21.504zM780.288 6.144q74.752 0 139.776 19.968t113.664 57.856 76.288 92.16 27.648 122.88q0 33.792-16.384 50.688t-35.328 17.408-35.328-14.336-16.384-45.568q0-40.96-22.528-77.824t-59.392-64.512-84.48-43.52-96.768-15.872q-31.744 0-47.104-15.36t-14.336-34.304 18.944-34.304 51.712-15.36zM780.288 169.984q95.232 0 144.384 48.64t49.152 146.944q0 30.72-10.24 43.52t-22.528 11.264-22.528-14.848-10.24-35.84q0-60.416-34.816-96.256t-93.184-35.84q-19.456 0-28.672-10.752t-9.216-23.04 9.728-23.04 28.16-10.752z" /></svg>
    </a>
    

    
    <a class="social-link" title="github" href="https://github.com/cizixs" target="_blank" rel="noopener">
        <svg viewBox="0 0 1049 1024" xmlns="http://www.w3.org/2000/svg"><path d="M524.979332 0C234.676191 0 0 234.676191 0 524.979332c0 232.068678 150.366597 428.501342 358.967656 498.035028 26.075132 5.215026 35.636014-11.299224 35.636014-25.205961 0-12.168395-0.869171-53.888607-0.869171-97.347161-146.020741 31.290159-176.441729-62.580318-176.441729-62.580318-23.467619-60.841976-58.234462-76.487055-58.234463-76.487055-47.804409-32.15933 3.476684-32.15933 3.476685-32.15933 53.019436 3.476684 80.83291 53.888607 80.83291 53.888607 46.935238 79.963739 122.553122 57.365291 152.97411 43.458554 4.345855-33.897672 18.252593-57.365291 33.028501-70.402857-116.468925-12.168395-239.022047-57.365291-239.022047-259.012982 0-57.365291 20.860106-104.300529 53.888607-140.805715-5.215026-13.037566-23.467619-66.926173 5.215027-139.067372 0 0 44.327725-13.906737 144.282399 53.888607 41.720212-11.299224 86.917108-17.383422 131.244833-17.383422s89.524621 6.084198 131.244833 17.383422C756.178839 203.386032 800.506564 217.29277 800.506564 217.29277c28.682646 72.1412 10.430053 126.029806 5.215026 139.067372 33.897672 36.505185 53.888607 83.440424 53.888607 140.805715 0 201.64769-122.553122 245.975415-239.891218 259.012982 19.121764 16.514251 35.636014 47.804409 35.636015 97.347161 0 70.402857-0.869171 126.898978-0.869172 144.282399 0 13.906737 9.560882 30.420988 35.636015 25.205961 208.601059-69.533686 358.967656-265.96635 358.967655-498.035028C1049.958663 234.676191 814.413301 0 524.979332 0z" /></svg>
    </a>
    

    
    <a class="social-link" title="stackoverflow" href="https://stackoverflow.com/users/1925083/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15 21h-10v-2h10v2zm6-11.665l-1.621-9.335-1.993.346 1.62 9.335 1.994-.346zm-5.964 6.937l-9.746-.975-.186 2.016 9.755.879.177-1.92zm.538-2.587l-9.276-2.608-.526 1.954 9.306 2.5.496-1.846zm1.204-2.413l-8.297-4.864-1.029 1.743 8.298 4.865 1.028-1.744zm1.866-1.467l-5.339-7.829-1.672 1.14 5.339 7.829 1.672-1.14zm-2.644 4.195v8h-12v-8h-2v10h16v-10h-2z"/></svg>
    </a>
    

    

    
    <a class="social-link" title="twitter" href="https://twitter.com/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>

    </a>
    

    
    <a class="social-link" title="instagram" href="https://www.instagram.com/cizixs/" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
    </a>
    
    
    
</div>
    </div>
</nav>
    </div>
</header>


<main id="site-main" class="site-main outer" role="main">
    <div class="inner">
        <header class="post-full-header">
            <section class="post-full-meta">
                <time  class="post-full-meta-date" datetime="2016-08-16T16:00:00.000Z" itemprop="datePublished">
                    2016-08-17
                </time>
                
                <span class="date-divider">/</span>
                
                <a href="/categories/blog/">blog</a>&nbsp;&nbsp;
                
                
            </section>
            <h1 class="post-full-title">go http 服务器编程</h1>
        </header>
        <article class="post-full no-image">
            
            <section class="post-full-content">
                <div id="lightgallery" class="markdown-body">
                    <h2 id="1-初识"><a href="#1-初识" class="headerlink" title="1. 初识"></a>1. 初识</h2><p>http 是典型的 C/S 架构，客户端向服务端发送请求（request），服务端做出应答（response）。</p>
<p>golang 的标准库 <code>net/http</code> 提供了 http 编程有关的接口，封装了内部TCP连接和报文解析的复杂琐碎的细节，使用者只需要和 <code>http.request</code> 和 <code>http.ResponseWriter</code> 两个对象交互就行。也就是说，我们只要写一个 handler，请求会通过参数传递进来，而它要做的就是根据请求的数据做处理，把结果写到 Response 中。废话不多说，来看看 hello world 程序有多简单吧！</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">"io"</span>
    <span class="token string">"net/http"</span>
<span class="token punctuation">)</span>

<span class="token keyword">type</span> helloHandler <span class="token keyword">struct</span><span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token punctuation">(</span>h <span class="token operator">*</span>helloHandler<span class="token punctuation">)</span> <span class="token function">ServeHTTP</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    w<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span><span class="token string">"Hello, world!"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    http<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>helloHandler<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
    http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":12345"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>运行 <code>go run hello_server.go</code>，我们的服务器就会监听在本地的 <code>12345</code> 端口，对所有的请求都会返回 <code>hello, world!</code>：</p>
<p><img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tKfTcgw1f6uoaz8reuj30ne0hrq56.jpg" alt=""></p>
<p>正如上面程序展示的那样，我们只要实现的一个 Handler，它的<a href="https://golang.org/pkg/net/http/#Handler" target="_blank" rel="noopener">接口原型</a>是（也就是说只要实现了 <code>ServeHTTP</code> 方法的对象都可以作为 Handler）：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">type</span> Handler <span class="token keyword">interface</span> <span class="token punctuation">{</span>
    <span class="token function">ServeHTTP</span><span class="token punctuation">(</span>ResponseWriter<span class="token punctuation">,</span> <span class="token operator">*</span>Request<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>然后，注册到对应的路由路径上就 OK 了。</p>
<p><code>http.HandleFunc</code>接受两个参数：第一个参数是字符串表示的 url 路径，第二个参数是该 url 实际的处理对象。</p>
<p><code>http.ListenAndServe</code> 监听在某个端口，启动服务，准备接受客户端的请求（第二个参数这里设置为 <code>nil</code>，这里也不要纠结什么意思，后面会有讲解）。每次客户端有请求的时候，把请求封装成 <code>http.Request</code>，调用对应的 handler 的 <code>ServeHTTP</code> 方法，然后把操作后的 <code>http.ResponseWriter</code> 解析，返回到客户端。</p>
<h2 id="2-封装"><a href="#2-封装" class="headerlink" title="2. 封装"></a>2. 封装</h2><p>上面的代码没有什么问题，但是有一个不便：每次写 Handler 的时候，都要定义一个类型，然后编写对应的 <code>ServeHTTP</code> 方法，这个步骤对于所有 Handler 都是一样的。重复的工作总是可以抽象出来，<code>net/http</code> 也正这么做了，它提供了 <code>http.HandleFunc</code> 方法，允许直接把特定类型的函数作为 handler。上面的代码可以改成：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">"io"</span>
    <span class="token string">"net/http"</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">helloHandler</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> req <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    io<span class="token punctuation">.</span><span class="token function">WriteString</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"hello, world!\n"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    http<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> helloHandler<span class="token punctuation">)</span>
    http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":12345"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>其实，<code>HandleFunc</code> 只是一个适配器，</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// The HandlerFunc type is an adapter to allow the use of</span>
<span class="token comment" spellcheck="true">// ordinary functions as HTTP handlers.  If f is a function</span>
<span class="token comment" spellcheck="true">// with the appropriate signature, HandlerFunc(f) is a</span>
<span class="token comment" spellcheck="true">// Handler object that calls f.</span>
<span class="token keyword">type</span> HandlerFunc <span class="token keyword">func</span><span class="token punctuation">(</span>ResponseWriter<span class="token punctuation">,</span> <span class="token operator">*</span>Request<span class="token punctuation">)</span>

<span class="token comment" spellcheck="true">// ServeHTTP calls f(w, r).</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>f HandlerFunc<span class="token punctuation">)</span> <span class="token function">ServeHTTP</span><span class="token punctuation">(</span>w ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">f</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> r<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>自动给 <code>f</code> 函数添加了 <code>HandlerFunc</code> 这个壳，最终调用的还是 <code>ServerHTTP</code>，只不过会直接使用 <code>f(w, r)</code>。这样封装的好处是：使用者可以专注于业务逻辑的编写，省去了很多重复的代码处理逻辑。如果只是简单的 Handler，会直接使用函数；如果是需要传递更多信息或者有复杂的操作，会使用上部分的方法。</p>
<p>如果需要我们自己写的话，是这样的：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">"io"</span>
    <span class="token string">"net/http"</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">helloHandler</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> req <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    io<span class="token punctuation">.</span><span class="token function">WriteString</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"hello, world!\n"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// 通过 HandlerFunc 把函数转换成 Handler 接口的实现对象</span>
    hh <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">HandlerFunc</span><span class="token punctuation">(</span>helloHandler<span class="token punctuation">)</span>
    http<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> hh<span class="token punctuation">)</span>
    http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":12345"</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<h2 id="3-默认"><a href="#3-默认" class="headerlink" title="3. 默认"></a>3. 默认</h2><p>大部分的服务器逻辑都需要使用者编写对应的 Handler，不过有些 Handler 使用频繁，因此 <code>net/http</code> 提供了它们的实现。比如负责文件 hosting 的 <code>FileServer</code>、负责 404 的<code>NotFoundHandler</code> 和 负责重定向的<code>RedirectHandler</code>。下面这个简单的例子，把当前目录所有文件 host 到服务端：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">"net/http"</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":12345"</span><span class="token punctuation">,</span> http<span class="token punctuation">.</span><span class="token function">FileServer</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span><span class="token function">Dir</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>强大吧！只要一行逻辑代码就能实现一个简单的静态文件服务器。从这里可以看出一件事：<code>http.ListenAndServe</code> 第二个参数就是一个 Handler 函数（请记住这一点，后面有些内容依赖于这个）。</p>
<p>运行这个程序，在浏览器中打开 <code>http://127.0.0.1:12345</code>，可以看到所有的文件，点击对应的文件还能看到它的内容。</p>
<p><img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tKfTcgw1f6uujkzgc9j30mc0e8gm5.jpg" alt=""></p>
<p>其他两个 Handler，这里就不再举例子了，读者可以自行参考文档。</p>
<h2 id="4-路由"><a href="#4-路由" class="headerlink" title="4. 路由"></a>4. 路由</h2><p>虽然上面的代码已经工作，并且能实现很多功能，但是实际开发中，HTTP 接口会有许多的 URL 和对应的 Handler。这里就要讲 <code>net/http</code> 的另外一个重要的概念：<code>ServeMux</code>。<code>Mux</code> 是 <code>multiplexor</code> 的缩写，就是多路传输的意思（请求传过来，根据某种判断，分流到后端多个不同的地方）。<code>ServeMux</code> 可以注册多了 URL 和 handler 的对应关系，并自动把请求转发到对应的 handler 进行处理。我们还是来看例子吧：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">"io"</span>
    <span class="token string">"net/http"</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">helloHandler</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    io<span class="token punctuation">.</span><span class="token function">WriteString</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"Hello, world!\n"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">echoHandler</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    io<span class="token punctuation">.</span><span class="token function">WriteString</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> r<span class="token punctuation">.</span>URL<span class="token punctuation">.</span>Path<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    mux <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">NewServeMux</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    mux<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/hello"</span><span class="token punctuation">,</span> helloHandler<span class="token punctuation">)</span>
    mux<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> echoHandler<span class="token punctuation">)</span>

    http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":12345"</span><span class="token punctuation">,</span> mux<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>这个服务器的功能也很简单：如果在请求的 URL 是 <code>/hello</code>，就返回 <code>hello, world!</code>；否则就返回 URL 的路径，路径是从请求对象 <code>http.Requests</code> 中提取的。</p>
<p><img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tKfTcgw1f6vdun05zpj30ne0hrgoe.jpg" alt=""></p>
<p>这段代码和之前的代码有两点区别：</p>
<ol>
<li>通过 <code>NewServeMux</code> 生成了 <code>ServerMux</code> 结构，URL 和 handler 是通过它注册的</li>
<li><code>http.ListenAndServe</code> 方法第二个参数变成了上面的 <code>mux</code> 变量</li>
</ol>
<p>还记得我们之前说过，<code>http.ListenAndServe</code> 第二个参数应该是 Handler 类型的变量吗？这里为什么能传过来 <code>ServeMux</code>？嗯，估计你也猜到啦：<code>ServeMux</code> 也是是 <code>Handler</code> 接口的实现，也就是说它实现了 <code>ServeHTTP</code> 方法，我们来看一下：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">type</span> ServeMux <span class="token keyword">struct</span> <span class="token punctuation">{</span>
        <span class="token comment" spellcheck="true">// contains filtered or unexported fields</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">NewServeMux</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span>ServeMux
<span class="token keyword">func</span> <span class="token punctuation">(</span>mux <span class="token operator">*</span>ServeMux<span class="token punctuation">)</span> <span class="token function">Handle</span><span class="token punctuation">(</span>pattern <span class="token builtin">string</span><span class="token punctuation">,</span> handler Handler<span class="token punctuation">)</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>mux <span class="token operator">*</span>ServeMux<span class="token punctuation">)</span> <span class="token function">HandleFunc</span><span class="token punctuation">(</span>pattern <span class="token builtin">string</span><span class="token punctuation">,</span> handler <span class="token keyword">func</span><span class="token punctuation">(</span>ResponseWriter<span class="token punctuation">,</span> <span class="token operator">*</span>Request<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>mux <span class="token operator">*</span>ServeMux<span class="token punctuation">)</span> <span class="token function">Handler</span><span class="token punctuation">(</span>r <span class="token operator">*</span>Request<span class="token punctuation">)</span> <span class="token punctuation">(</span>h Handler<span class="token punctuation">,</span> pattern <span class="token builtin">string</span><span class="token punctuation">)</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>mux <span class="token operator">*</span>ServeMux<span class="token punctuation">)</span> <span class="token function">ServeHTTP</span><span class="token punctuation">(</span>w ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>Request<span class="token punctuation">)</span>
</code></pre>
<p>哈！果然，这里的方法我们大都很熟悉，除了 <code>Handler()</code> 返回某个请求的 Handler。<code>Handle</code> 和 <code>HandleFunc</code> 这两个方法 <code>net/http</code> 也提供了，后面我们会说明它们之间的关系。而 <code>ServeHTTP</code> 就是 <code>ServeMux</code> 的核心处理逻辑：<strong>根据传递过来的 Request，匹配之前注册的 URL 和处理函数，找到最匹配的项，进行处理。</strong>可以说 <code>ServeMux</code> 是个特殊的 Handler，它负责路由和调用其他后端 Handler 的处理方法。</p>
<p>关于<code>ServeMux</code> ，有几点要说明：</p>
<ul>
<li>URL 分为两种，末尾是 <code>/</code>：表示一个子树，后面可以跟其他子路径； 末尾不是 <code>/</code>，表示一个叶子，固定的路径</li>
<li>以<code>/</code> 结尾的 URL 可以匹配它的任何子路径，比如 <code>/images</code> 会匹配 <code>/images/cute-cat.jpg</code></li>
<li>它采用最长匹配原则，如果有多个匹配，一定采用匹配路径最长的那个进行处理</li>
<li>如果没有找到任何匹配项，会返回 404 错误</li>
<li><code>ServeMux</code> 也会识别和处理 <code>.</code> 和 <code>..</code>，正确转换成对应的 URL 地址</li>
</ul>
<p>你可能会有疑问？我们之间为什么没有使用 <code>ServeMux</code> 就能实现路径功能？那是因为 <code>net/http</code> 在后台默认创建使用了 <code>DefaultServeMux</code>。</p>
<h2 id="5-深入"><a href="#5-深入" class="headerlink" title="5. 深入"></a>5. 深入</h2><p>嗯，上面基本覆盖了编写 HTTP 服务端需要的所有内容。这部分就分析一下，它们的源码实现，加深理解，以后遇到疑惑也能通过源码来定位和解决。</p>
<h3 id="Server"><a href="#Server" class="headerlink" title="Server"></a>Server</h3><p>首先来看 <code>http.ListenAndServe()</code>:</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">ListenAndServe</span><span class="token punctuation">(</span>addr <span class="token builtin">string</span><span class="token punctuation">,</span> handler Handler<span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span>
    server <span class="token operator">:=</span> <span class="token operator">&amp;</span>Server<span class="token punctuation">{</span>Addr<span class="token punctuation">:</span> addr<span class="token punctuation">,</span> Handler<span class="token punctuation">:</span> handler<span class="token punctuation">}</span>
    <span class="token keyword">return</span> server<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>这个函数其实也是一层封装，创建了 <code>Server</code> 结构，并调用它的 <code>ListenAndServe</code> 方法，那我们就跟进去看看：</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// A Server defines parameters for running an HTTP server.</span>
<span class="token comment" spellcheck="true">// The zero value for Server is a valid configuration.</span>
<span class="token keyword">type</span> Server <span class="token keyword">struct</span> <span class="token punctuation">{</span>
    Addr           <span class="token builtin">string</span>        <span class="token comment" spellcheck="true">// TCP address to listen on, ":http" if empty</span>
    Handler        Handler       <span class="token comment" spellcheck="true">// handler to invoke, http.DefaultServeMux if nil</span>
    <span class="token operator">...</span><span class="token operator">...</span>
<span class="token punctuation">}</span>

<span class="token comment" spellcheck="true">// ListenAndServe listens on the TCP network address srv.Addr and then</span>
<span class="token comment" spellcheck="true">// calls Serve to handle requests on incoming connections.  If</span>
<span class="token comment" spellcheck="true">// srv.Addr is blank, ":http" is used.</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>srv <span class="token operator">*</span>Server<span class="token punctuation">)</span> <span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span>
    addr <span class="token operator">:=</span> srv<span class="token punctuation">.</span>Addr
    <span class="token keyword">if</span> addr <span class="token operator">==</span> <span class="token string">""</span> <span class="token punctuation">{</span>
        addr <span class="token operator">=</span> <span class="token string">":http"</span>
    <span class="token punctuation">}</span>
    ln<span class="token punctuation">,</span> err <span class="token operator">:=</span> net<span class="token punctuation">.</span><span class="token function">Listen</span><span class="token punctuation">(</span><span class="token string">"tcp"</span><span class="token punctuation">,</span> addr<span class="token punctuation">)</span>
    <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> err
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> srv<span class="token punctuation">.</span><span class="token function">Serve</span><span class="token punctuation">(</span>tcpKeepAliveListener<span class="token punctuation">{</span>ln<span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token operator">*</span>net<span class="token punctuation">.</span>TCPListener<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p><code>Server</code> 保存了运行 HTTP 服务需要的参数，调用 <code>net.Listen</code> 监听在对应的 tcp 端口，<code>tcpKeepAliveListener</code> 设置了 TCP 的 <code>KeepAlive</code> 功能，最后调用 <code>srv.Serve()</code>方法开始真正的循环逻辑。我们再跟进去看看 <code>Serve</code> 方法：</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// Serve accepts incoming connections on the Listener l, creating a</span>
<span class="token comment" spellcheck="true">// new service goroutine for each.  The service goroutines read requests and</span>
<span class="token comment" spellcheck="true">// then call srv.Handler to reply to them.</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>srv <span class="token operator">*</span>Server<span class="token punctuation">)</span> <span class="token function">Serve</span><span class="token punctuation">(</span>l net<span class="token punctuation">.</span>Listener<span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span>
    <span class="token keyword">defer</span> l<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">var</span> tempDelay time<span class="token punctuation">.</span>Duration <span class="token comment" spellcheck="true">// how long to sleep on accept failure</span>
    <span class="token comment" spellcheck="true">// 循环逻辑，接受请求并处理</span>
    <span class="token keyword">for</span> <span class="token punctuation">{</span>
         <span class="token comment" spellcheck="true">// 有新的连接</span>
        rw<span class="token punctuation">,</span> e <span class="token operator">:=</span> l<span class="token punctuation">.</span><span class="token function">Accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> e <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> ne<span class="token punctuation">,</span> ok <span class="token operator">:=</span> e<span class="token punctuation">.</span><span class="token punctuation">(</span>net<span class="token punctuation">.</span>Error<span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token operator">&amp;&amp;</span> ne<span class="token punctuation">.</span><span class="token function">Temporary</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">if</span> tempDelay <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span>
                    tempDelay <span class="token operator">=</span> <span class="token number">5</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Millisecond
                <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
                    tempDelay <span class="token operator">*=</span> <span class="token number">2</span>
                <span class="token punctuation">}</span>
                <span class="token keyword">if</span> max <span class="token operator">:=</span> <span class="token number">1</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">;</span> tempDelay <span class="token operator">></span> max <span class="token punctuation">{</span>
                    tempDelay <span class="token operator">=</span> max
                <span class="token punctuation">}</span>
                srv<span class="token punctuation">.</span><span class="token function">logf</span><span class="token punctuation">(</span><span class="token string">"http: Accept error: %v; retrying in %v"</span><span class="token punctuation">,</span> e<span class="token punctuation">,</span> tempDelay<span class="token punctuation">)</span>
                time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>tempDelay<span class="token punctuation">)</span>
                <span class="token keyword">continue</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">return</span> e
        <span class="token punctuation">}</span>
        tempDelay <span class="token operator">=</span> <span class="token number">0</span>
         <span class="token comment" spellcheck="true">// 创建 Conn 连接</span>
        c<span class="token punctuation">,</span> err <span class="token operator">:=</span> srv<span class="token punctuation">.</span><span class="token function">newConn</span><span class="token punctuation">(</span>rw<span class="token punctuation">)</span>
        <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
            <span class="token keyword">continue</span>
        <span class="token punctuation">}</span>
        c<span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>rwc<span class="token punctuation">,</span> StateNew<span class="token punctuation">)</span> <span class="token comment" spellcheck="true">// before Serve can return</span>
         <span class="token comment" spellcheck="true">// 启动新的 goroutine 进行处理</span>
        <span class="token keyword">go</span> c<span class="token punctuation">.</span><span class="token function">serve</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>最上面的注释也说明了这个方法的主要功能：</p>
<ul>
<li>接受 <code>Listener l</code> 传递过来的请求</li>
<li>为每个请求创建 goroutine 进行后台处理</li>
<li>goroutine 会读取请求，调用 <code>srv.Handler</code></li>
</ul>
<pre class=" language-go"><code class="language-go"><span class="token keyword">func</span> <span class="token punctuation">(</span>c <span class="token operator">*</span>conn<span class="token punctuation">)</span> <span class="token function">serve</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    origConn <span class="token operator">:=</span> c<span class="token punctuation">.</span>rwc <span class="token comment" spellcheck="true">// copy it before it's set nil on Close or Hijack</span>

      <span class="token operator">...</span>

    <span class="token keyword">for</span> <span class="token punctuation">{</span>
        w<span class="token punctuation">,</span> err <span class="token operator">:=</span> c<span class="token punctuation">.</span><span class="token function">readRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> c<span class="token punctuation">.</span>lr<span class="token punctuation">.</span>N <span class="token operator">!=</span> c<span class="token punctuation">.</span>server<span class="token punctuation">.</span><span class="token function">initialLimitedReaderSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment" spellcheck="true">// If we read any bytes off the wire, we're active.</span>
            c<span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>rwc<span class="token punctuation">,</span> StateActive<span class="token punctuation">)</span>
        <span class="token punctuation">}</span>

         <span class="token operator">...</span>

        <span class="token comment" spellcheck="true">// HTTP cannot have multiple simultaneous active requests.[*]</span>
        <span class="token comment" spellcheck="true">// Until the server replies to this request, it can't read another,</span>
        <span class="token comment" spellcheck="true">// so we might as well run the handler in this goroutine.</span>
        <span class="token comment" spellcheck="true">// [*] Not strictly true: HTTP pipelining.  We could let them all process</span>
        <span class="token comment" spellcheck="true">// in parallel even if their responses need to be serialized.</span>
        serverHandler<span class="token punctuation">{</span>c<span class="token punctuation">.</span>server<span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">ServeHTTP</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> w<span class="token punctuation">.</span>req<span class="token punctuation">)</span>

        w<span class="token punctuation">.</span><span class="token function">finishRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> w<span class="token punctuation">.</span>closeAfterReply <span class="token punctuation">{</span>
            <span class="token keyword">if</span> w<span class="token punctuation">.</span>requestBodyLimitHit <span class="token punctuation">{</span>
                c<span class="token punctuation">.</span><span class="token function">closeWriteAndWait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">break</span>
        <span class="token punctuation">}</span>
        c<span class="token punctuation">.</span><span class="token function">setState</span><span class="token punctuation">(</span>c<span class="token punctuation">.</span>rwc<span class="token punctuation">,</span> StateIdle<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>看到上面这段代码 <code>serverHandler{c.server}.ServeHTTP(w, w.req)</code>这一句了吗？它会调用最早传递给 <code>Server</code> 的 Handler 函数：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">func</span> <span class="token punctuation">(</span>sh serverHandler<span class="token punctuation">)</span> <span class="token function">ServeHTTP</span><span class="token punctuation">(</span>rw ResponseWriter<span class="token punctuation">,</span> req <span class="token operator">*</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    handler <span class="token operator">:=</span> sh<span class="token punctuation">.</span>srv<span class="token punctuation">.</span>Handler
    <span class="token keyword">if</span> handler <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
        handler <span class="token operator">=</span> DefaultServeMux
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> req<span class="token punctuation">.</span>RequestURI <span class="token operator">==</span> <span class="token string">"*"</span> <span class="token operator">&amp;&amp;</span> req<span class="token punctuation">.</span>Method <span class="token operator">==</span> <span class="token string">"OPTIONS"</span> <span class="token punctuation">{</span>
        handler <span class="token operator">=</span> globalOptionsHandler<span class="token punctuation">{</span><span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    handler<span class="token punctuation">.</span><span class="token function">ServeHTTP</span><span class="token punctuation">(</span>rw<span class="token punctuation">,</span> req<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>哇！这里看到 <code>DefaultServeMux</code> 了吗？如果没有 handler 为空，就会使用它。<code>handler.ServeHTTP(rw, req)</code>，Handler 接口都要实现 <code>ServeHTTP</code> 这个方法，因为这里就要被调用啦。</p>
<p>也就是说，无论如何，最终都会用到 <code>ServeMux</code>，也就是负责 URL 路由的家伙。前面也已经说过，它的 <code>ServeHTTP</code> 方法就是根据请求的路径，把它转交给注册的 handler 进行处理。这次，我们就在源码层面一探究竟。</p>
<h3 id="ServeMux"><a href="#ServeMux" class="headerlink" title="ServeMux"></a>ServeMux</h3><p>我们已经知道，<code>ServeMux</code> 会以某种方式保存 URL 和 Handlers 的对应关系，下面我们就从代码层面来解开这个秘密：</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">type</span> ServeMux <span class="token keyword">struct</span> <span class="token punctuation">{</span>
    mu    sync<span class="token punctuation">.</span>RWMutex
    m     <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span>muxEntry  <span class="token comment" spellcheck="true">// 存放路由信息的字典！\(^o^)/</span>
    hosts <span class="token builtin">bool</span> <span class="token comment" spellcheck="true">// whether any patterns contain hostnames</span>
<span class="token punctuation">}</span>

<span class="token keyword">type</span> muxEntry <span class="token keyword">struct</span> <span class="token punctuation">{</span>
    explicit <span class="token builtin">bool</span>
    h        Handler
    pattern  <span class="token builtin">string</span>
<span class="token punctuation">}</span>
</code></pre>
<p>没错，数据结构也比较直观，和我们想象的差不多，路由信息保存在字典中，接下来就看看几个重要的操作：路由信息是怎么注册的？<code>ServeHTTP</code> 方法到底是怎么做的？路由查找过程是怎样的？</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// Handle registers the handler for the given pattern.</span>
<span class="token comment" spellcheck="true">// If a handler already exists for pattern, Handle panics.</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>mux <span class="token operator">*</span>ServeMux<span class="token punctuation">)</span> <span class="token function">Handle</span><span class="token punctuation">(</span>pattern <span class="token builtin">string</span><span class="token punctuation">,</span> handler Handler<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    mux<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">defer</span> mux<span class="token punctuation">.</span>mu<span class="token punctuation">.</span><span class="token function">Unlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token comment" spellcheck="true">// 边界情况处理</span>
    <span class="token keyword">if</span> pattern <span class="token operator">==</span> <span class="token string">""</span> <span class="token punctuation">{</span>
        <span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">"http: invalid pattern "</span> <span class="token operator">+</span> pattern<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> handler <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
        <span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">"http: nil handler"</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> mux<span class="token punctuation">.</span>m<span class="token punctuation">[</span>pattern<span class="token punctuation">]</span><span class="token punctuation">.</span>explicit <span class="token punctuation">{</span>
        <span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">"http: multiple registrations for "</span> <span class="token operator">+</span> pattern<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>

    <span class="token comment" spellcheck="true">// 创建 `muxEntry` 并添加到路由字典中</span>
    mux<span class="token punctuation">.</span>m<span class="token punctuation">[</span>pattern<span class="token punctuation">]</span> <span class="token operator">=</span> muxEntry<span class="token punctuation">{</span>explicit<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> h<span class="token punctuation">:</span> handler<span class="token punctuation">,</span> pattern<span class="token punctuation">:</span> pattern<span class="token punctuation">}</span>

    <span class="token keyword">if</span> pattern<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">!=</span> <span class="token string">'/'</span> <span class="token punctuation">{</span>
        mux<span class="token punctuation">.</span>hosts <span class="token operator">=</span> <span class="token boolean">true</span>
    <span class="token punctuation">}</span>

    <span class="token comment" spellcheck="true">// 这是一个很有用的小技巧，如果注册了 `/tree/`， `serveMux` 会自动添加一个 `/tree` 的路径并重定向到 `/tree/`。当然这个 `/tree` 路径会被用户显示的路由信息覆盖。</span>
    <span class="token comment" spellcheck="true">// Helpful behavior:</span>
    <span class="token comment" spellcheck="true">// If pattern is /tree/, insert an implicit permanent redirect for /tree.</span>
    <span class="token comment" spellcheck="true">// It can be overridden by an explicit registration.</span>
    n <span class="token operator">:=</span> <span class="token function">len</span><span class="token punctuation">(</span>pattern<span class="token punctuation">)</span>
    <span class="token keyword">if</span> n <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> pattern<span class="token punctuation">[</span>n<span class="token number">-1</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">'/'</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>mux<span class="token punctuation">.</span>m<span class="token punctuation">[</span>pattern<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">:</span>n<span class="token number">-1</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">.</span>explicit <span class="token punctuation">{</span>
        <span class="token comment" spellcheck="true">// If pattern contains a host name, strip it and use remaining</span>
        <span class="token comment" spellcheck="true">// path for redirect.</span>
        path <span class="token operator">:=</span> pattern
        <span class="token keyword">if</span> pattern<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">!=</span> <span class="token string">'/'</span> <span class="token punctuation">{</span>
            <span class="token comment" spellcheck="true">// In pattern, at least the last character is a '/', so</span>
            <span class="token comment" spellcheck="true">// strings.Index can't be -1.</span>
            path <span class="token operator">=</span> pattern<span class="token punctuation">[</span>strings<span class="token punctuation">.</span><span class="token function">Index</span><span class="token punctuation">(</span>pattern<span class="token punctuation">,</span> <span class="token string">"/"</span><span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token punctuation">]</span>
        <span class="token punctuation">}</span>
        mux<span class="token punctuation">.</span>m<span class="token punctuation">[</span>pattern<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">:</span>n<span class="token number">-1</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> muxEntry<span class="token punctuation">{</span>h<span class="token punctuation">:</span> <span class="token function">RedirectHandler</span><span class="token punctuation">(</span>path<span class="token punctuation">,</span> StatusMovedPermanently<span class="token punctuation">)</span><span class="token punctuation">,</span> pattern<span class="token punctuation">:</span> pattern<span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>路由注册没有什么特殊的地方，很简单，也符合我们的预期，注意最后一段代码对类似 <code>/tree</code> URL 重定向的处理。</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// ServeHTTP dispatches the request to the handler whose</span>
<span class="token comment" spellcheck="true">// pattern most closely matches the request URL.</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>mux <span class="token operator">*</span>ServeMux<span class="token punctuation">)</span> <span class="token function">ServeHTTP</span><span class="token punctuation">(</span>w ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> r<span class="token punctuation">.</span>RequestURI <span class="token operator">==</span> <span class="token string">"*"</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> r<span class="token punctuation">.</span><span class="token function">ProtoAtLeast</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            w<span class="token punctuation">.</span><span class="token function">Header</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"Connection"</span><span class="token punctuation">,</span> <span class="token string">"close"</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
        w<span class="token punctuation">.</span><span class="token function">WriteHeader</span><span class="token punctuation">(</span>StatusBadRequest<span class="token punctuation">)</span>
        <span class="token keyword">return</span>
    <span class="token punctuation">}</span>
    h<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> mux<span class="token punctuation">.</span><span class="token function">Handler</span><span class="token punctuation">(</span>r<span class="token punctuation">)</span>
    h<span class="token punctuation">.</span><span class="token function">ServeHTTP</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> r<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>好吧，<code>ServeHTTP</code> 也只是通过 <code>mux.Handler(r)</code> 找到请求对应的 handler，调用它的 <code>ServeHTTP</code> 方法，代码比较简单我们就显示了，它最终会调用 <code>mux.match()</code> 方法，我们来看一下它的实现：</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// Does path match pattern?</span>
<span class="token keyword">func</span> <span class="token function">pathMatch</span><span class="token punctuation">(</span>pattern<span class="token punctuation">,</span> path <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token builtin">bool</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token function">len</span><span class="token punctuation">(</span>pattern<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span>
        <span class="token comment" spellcheck="true">// should not happen</span>
        <span class="token keyword">return</span> <span class="token boolean">false</span>
    <span class="token punctuation">}</span>
    n <span class="token operator">:=</span> <span class="token function">len</span><span class="token punctuation">(</span>pattern<span class="token punctuation">)</span>
    <span class="token keyword">if</span> pattern<span class="token punctuation">[</span>n<span class="token number">-1</span><span class="token punctuation">]</span> <span class="token operator">!=</span> <span class="token string">'/'</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> pattern <span class="token operator">==</span> path
    <span class="token punctuation">}</span>
    <span class="token comment" spellcheck="true">// 匹配的逻辑很简单，path 前面的字符和 pattern 一样就是匹配</span>
    <span class="token keyword">return</span> <span class="token function">len</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span> <span class="token operator">>=</span> n <span class="token operator">&amp;&amp;</span> path<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">:</span>n<span class="token punctuation">]</span> <span class="token operator">==</span> pattern
<span class="token punctuation">}</span>

<span class="token comment" spellcheck="true">// Find a handler on a handler map given a path string</span>
<span class="token comment" spellcheck="true">// Most-specific (longest) pattern wins</span>
<span class="token keyword">func</span> <span class="token punctuation">(</span>mux <span class="token operator">*</span>ServeMux<span class="token punctuation">)</span> <span class="token function">match</span><span class="token punctuation">(</span>path <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>h Handler<span class="token punctuation">,</span> pattern <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">var</span> n <span class="token operator">=</span> <span class="token number">0</span>
    <span class="token keyword">for</span> k<span class="token punctuation">,</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> mux<span class="token punctuation">.</span>m <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token operator">!</span><span class="token function">pathMatch</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span> path<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">continue</span>
        <span class="token punctuation">}</span>
         <span class="token comment" spellcheck="true">// 最长匹配的逻辑在这里</span>
        <span class="token keyword">if</span> h <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token operator">||</span> <span class="token function">len</span><span class="token punctuation">(</span>k<span class="token punctuation">)</span> <span class="token operator">></span> n <span class="token punctuation">{</span>
            n <span class="token operator">=</span> <span class="token function">len</span><span class="token punctuation">(</span>k<span class="token punctuation">)</span>
            h <span class="token operator">=</span> v<span class="token punctuation">.</span>h
            pattern <span class="token operator">=</span> v<span class="token punctuation">.</span>pattern
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span>
<span class="token punctuation">}</span>
</code></pre>
<p><code>match</code> 会遍历路由信息字典，找到所有匹配该路径最长的那个。路由部分的代码解释就到这里了，最后回答上面的一个问题：<code>http.HandleFunc</code> 和 <code>ServeMux.HandlerFunc</code> 是什么关系？</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// Handle registers the handler for the given pattern</span>
<span class="token comment" spellcheck="true">// in the DefaultServeMux.</span>
<span class="token comment" spellcheck="true">// The documentation for ServeMux explains how patterns are matched.</span>
<span class="token keyword">func</span> <span class="token function">Handle</span><span class="token punctuation">(</span>pattern <span class="token builtin">string</span><span class="token punctuation">,</span> handler Handler<span class="token punctuation">)</span> <span class="token punctuation">{</span> DefaultServeMux<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span>pattern<span class="token punctuation">,</span> handler<span class="token punctuation">)</span> <span class="token punctuation">}</span>

<span class="token comment" spellcheck="true">// HandleFunc registers the handler function for the given pattern</span>
<span class="token comment" spellcheck="true">// in the DefaultServeMux.</span>
<span class="token comment" spellcheck="true">// The documentation for ServeMux explains how patterns are matched.</span>
<span class="token keyword">func</span> <span class="token function">HandleFunc</span><span class="token punctuation">(</span>pattern <span class="token builtin">string</span><span class="token punctuation">,</span> handler <span class="token keyword">func</span><span class="token punctuation">(</span>ResponseWriter<span class="token punctuation">,</span> <span class="token operator">*</span>Request<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    DefaultServeMux<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span>pattern<span class="token punctuation">,</span> handler<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p> 原来是直接通过 <code>DefaultServeMux</code> 调用对应的方法，到这里上面的一切都串起来了！</p>
<h3 id="Request"><a href="#Request" class="headerlink" title="Request"></a>Request</h3><p>最后一部分，要讲讲 Handler 函数接受的两个参数：<code>http.Request</code> 和 <code>http.ResponseWriter</code>。</p>
<p>Request 就是封装好的客户端请求，包括 URL，method，header 等等所有信息，以及一些方便使用的方法：</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// A Request represents an HTTP request received by a server</span>
<span class="token comment" spellcheck="true">// or to be sent by a client.</span>
<span class="token comment" spellcheck="true">//</span>
<span class="token comment" spellcheck="true">// The field semantics differ slightly between client and server</span>
<span class="token comment" spellcheck="true">// usage. In addition to the notes on the fields below, see the</span>
<span class="token comment" spellcheck="true">// documentation for Request.Write and RoundTripper.</span>
<span class="token keyword">type</span> Request <span class="token keyword">struct</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// Method specifies the HTTP method (GET, POST, PUT, etc.).</span>
    <span class="token comment" spellcheck="true">// For client requests an empty string means GET.</span>
    Method <span class="token builtin">string</span>

    <span class="token comment" spellcheck="true">// URL specifies either the URI being requested (for server</span>
    <span class="token comment" spellcheck="true">// requests) or the URL to access (for client requests).</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// For server requests the URL is parsed from the URI</span>
    <span class="token comment" spellcheck="true">// supplied on the Request-Line as stored in RequestURI.  For</span>
    <span class="token comment" spellcheck="true">// most requests, fields other than Path and RawQuery will be</span>
    <span class="token comment" spellcheck="true">// empty. (See RFC 2616, Section 5.1.2)</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// For client requests, the URL's Host specifies the server to</span>
    <span class="token comment" spellcheck="true">// connect to, while the Request's Host field optionally</span>
    <span class="token comment" spellcheck="true">// specifies the Host header value to send in the HTTP</span>
    <span class="token comment" spellcheck="true">// request.</span>
    URL <span class="token operator">*</span>url<span class="token punctuation">.</span>URL

    <span class="token comment" spellcheck="true">// The protocol version for incoming requests.</span>
    <span class="token comment" spellcheck="true">// Client requests always use HTTP/1.1.</span>
    Proto      <span class="token builtin">string</span> <span class="token comment" spellcheck="true">// "HTTP/1.0"</span>
    ProtoMajor <span class="token builtin">int</span>    <span class="token comment" spellcheck="true">// 1</span>
    ProtoMinor <span class="token builtin">int</span>    <span class="token comment" spellcheck="true">// 0</span>

    <span class="token comment" spellcheck="true">// A header maps request lines to their values.</span>
    <span class="token comment" spellcheck="true">// If the header says</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">//    accept-encoding: gzip, deflate</span>
    <span class="token comment" spellcheck="true">//    Accept-Language: en-us</span>
    <span class="token comment" spellcheck="true">//    Connection: keep-alive</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// then</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">//    Header = map[string][]string{</span>
    <span class="token comment" spellcheck="true">//        "Accept-Encoding": {"gzip, deflate"},</span>
    <span class="token comment" spellcheck="true">//        "Accept-Language": {"en-us"},</span>
    <span class="token comment" spellcheck="true">//        "Connection": {"keep-alive"},</span>
    <span class="token comment" spellcheck="true">//    }</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// HTTP defines that header names are case-insensitive.</span>
    <span class="token comment" spellcheck="true">// The request parser implements this by canonicalizing the</span>
    <span class="token comment" spellcheck="true">// name, making the first character and any characters</span>
    <span class="token comment" spellcheck="true">// following a hyphen uppercase and the rest lowercase.</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// For client requests certain headers are automatically</span>
    <span class="token comment" spellcheck="true">// added and may override values in Header.</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// See the documentation for the Request.Write method.</span>
    Header Header

    <span class="token comment" spellcheck="true">// Body is the request's body.</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// For client requests a nil body means the request has no</span>
    <span class="token comment" spellcheck="true">// body, such as a GET request. The HTTP Client's Transport</span>
    <span class="token comment" spellcheck="true">// is responsible for calling the Close method.</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// For server requests the Request Body is always non-nil</span>
    <span class="token comment" spellcheck="true">// but will return EOF immediately when no body is present.</span>
    <span class="token comment" spellcheck="true">// The Server will close the request body. The ServeHTTP</span>
    <span class="token comment" spellcheck="true">// Handler does not need to.</span>
    Body io<span class="token punctuation">.</span>ReadCloser

    <span class="token comment" spellcheck="true">// ContentLength records the length of the associated content.</span>
    <span class="token comment" spellcheck="true">// The value -1 indicates that the length is unknown.</span>
    <span class="token comment" spellcheck="true">// Values >= 0 indicate that the given number of bytes may</span>
    <span class="token comment" spellcheck="true">// be read from Body.</span>
    <span class="token comment" spellcheck="true">// For client requests, a value of 0 means unknown if Body is not nil.</span>
    ContentLength <span class="token builtin">int64</span>

    <span class="token comment" spellcheck="true">// TransferEncoding lists the transfer encodings from outermost to</span>
    <span class="token comment" spellcheck="true">// innermost. An empty list denotes the "identity" encoding.</span>
    <span class="token comment" spellcheck="true">// TransferEncoding can usually be ignored; chunked encoding is</span>
    <span class="token comment" spellcheck="true">// automatically added and removed as necessary when sending and</span>
    <span class="token comment" spellcheck="true">// receiving requests.</span>
    TransferEncoding <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span>

    <span class="token comment" spellcheck="true">// Close indicates whether to close the connection after</span>
    <span class="token comment" spellcheck="true">// replying to this request (for servers) or after sending</span>
    <span class="token comment" spellcheck="true">// the request (for clients).</span>
    Close <span class="token builtin">bool</span>

    <span class="token comment" spellcheck="true">// For server requests Host specifies the host on which the</span>
    <span class="token comment" spellcheck="true">// URL is sought. Per RFC 2616, this is either the value of</span>
    <span class="token comment" spellcheck="true">// the "Host" header or the host name given in the URL itself.</span>
    <span class="token comment" spellcheck="true">// It may be of the form "host:port".</span>
    <span class="token comment" spellcheck="true">//</span>
    <span class="token comment" spellcheck="true">// For client requests Host optionally overrides the Host</span>
    <span class="token comment" spellcheck="true">// header to send. If empty, the Request.Write method uses</span>
    <span class="token comment" spellcheck="true">// the value of URL.Host.</span>
    Host <span class="token builtin">string</span>

    <span class="token comment" spellcheck="true">// Form contains the parsed form data, including both the URL</span>
    <span class="token comment" spellcheck="true">// field's query parameters and the POST or PUT form data.</span>
    <span class="token comment" spellcheck="true">// This field is only available after ParseForm is called.</span>
    <span class="token comment" spellcheck="true">// The HTTP client ignores Form and uses Body instead.</span>
    Form url<span class="token punctuation">.</span>Values

    <span class="token comment" spellcheck="true">// PostForm contains the parsed form data from POST or PUT</span>
    <span class="token comment" spellcheck="true">// body parameters.</span>
    <span class="token comment" spellcheck="true">// This field is only available after ParseForm is called.</span>
    <span class="token comment" spellcheck="true">// The HTTP client ignores PostForm and uses Body instead.</span>
    PostForm url<span class="token punctuation">.</span>Values

    <span class="token comment" spellcheck="true">// MultipartForm is the parsed multipart form, including file uploads.</span>
    <span class="token comment" spellcheck="true">// This field is only available after ParseMultipartForm is called.</span>
    <span class="token comment" spellcheck="true">// The HTTP client ignores MultipartForm and uses Body instead.</span>
    MultipartForm <span class="token operator">*</span>multipart<span class="token punctuation">.</span>Form

    <span class="token operator">...</span>

    <span class="token comment" spellcheck="true">// RemoteAddr allows HTTP servers and other software to record</span>
    <span class="token comment" spellcheck="true">// the network address that sent the request, usually for</span>
    <span class="token comment" spellcheck="true">// logging. This field is not filled in by ReadRequest and</span>
    <span class="token comment" spellcheck="true">// has no defined format. The HTTP server in this package</span>
    <span class="token comment" spellcheck="true">// sets RemoteAddr to an "IP:port" address before invoking a</span>
    <span class="token comment" spellcheck="true">// handler.</span>
    <span class="token comment" spellcheck="true">// This field is ignored by the HTTP client.</span>
    RemoteAddr <span class="token builtin">string</span>
    <span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Handler 需要知道关于请求的任何信息，都要从这个对象中获取，一般不会直接修改这个对象（除非你非常清楚自己在做什么）！</p>
<h3 id="ResponseWriter"><a href="#ResponseWriter" class="headerlink" title="ResponseWriter"></a>ResponseWriter</h3><p>ResponseWriter 是一个接口，定义了三个方法：</p>
<ul>
<li><code>Header()</code>：返回一个 Header 对象，可以通过它的 <code>Set()</code> 方法设置头部，注意最终返回的头部信息可能和你写进去的不完全相同，因为后续处理还可能修改头部的值（比如设置 <code>Content-Length</code>、<code>Content-type</code> 等操作）</li>
<li><code>Write()</code>： 写 response 的主体部分，比如 <code>html</code>  或者 <code>json</code> 的内容就是放到这里的</li>
<li><code>WriteHeader()</code>：设置 status code，如果没有调用这个函数，默认设置为 <code>http.StatusOK</code>， 就是 <code>200</code> 状态码</li>
</ul>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// A ResponseWriter interface is used by an HTTP handler to</span>
<span class="token comment" spellcheck="true">// construct an HTTP response.</span>
<span class="token keyword">type</span> ResponseWriter <span class="token keyword">interface</span> <span class="token punctuation">{</span>
    <span class="token comment" spellcheck="true">// Header returns the header map that will be sent by WriteHeader.</span>
    <span class="token comment" spellcheck="true">// Changing the header after a call to WriteHeader (or Write) has</span>
    <span class="token comment" spellcheck="true">// no effect.</span>
    <span class="token function">Header</span><span class="token punctuation">(</span><span class="token punctuation">)</span> Header

    <span class="token comment" spellcheck="true">// Write writes the data to the connection as part of an HTTP reply.</span>
    <span class="token comment" spellcheck="true">// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK)</span>
    <span class="token comment" spellcheck="true">// before writing the data.  If the Header does not contain a</span>
    <span class="token comment" spellcheck="true">// Content-Type line, Write adds a Content-Type set to the result of passing</span>
    <span class="token comment" spellcheck="true">// the initial 512 bytes of written data to DetectContentType.</span>
    <span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">byte</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span>

    <span class="token comment" spellcheck="true">// WriteHeader sends an HTTP response header with status code.</span>
    <span class="token comment" spellcheck="true">// If WriteHeader is not called explicitly, the first call to Write</span>
    <span class="token comment" spellcheck="true">// will trigger an implicit WriteHeader(http.StatusOK).</span>
    <span class="token comment" spellcheck="true">// Thus explicit calls to WriteHeader are mainly used to</span>
    <span class="token comment" spellcheck="true">// send error codes.</span>
    <span class="token function">WriteHeader</span><span class="token punctuation">(</span><span class="token builtin">int</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>实际上传递给 Handler 的对象是:</p>
<pre class=" language-go"><code class="language-go"><span class="token comment" spellcheck="true">// A response represents the server side of an HTTP response.</span>
<span class="token keyword">type</span> response <span class="token keyword">struct</span> <span class="token punctuation">{</span>
    conn          <span class="token operator">*</span>conn
    req           <span class="token operator">*</span>Request <span class="token comment" spellcheck="true">// request for this response</span>
    wroteHeader   <span class="token builtin">bool</span>     <span class="token comment" spellcheck="true">// reply header has been (logically) written</span>
    wroteContinue <span class="token builtin">bool</span>     <span class="token comment" spellcheck="true">// 100 Continue response was written</span>

    w  <span class="token operator">*</span>bufio<span class="token punctuation">.</span>Writer <span class="token comment" spellcheck="true">// buffers output in chunks to chunkWriter</span>
    cw chunkWriter
    sw <span class="token operator">*</span>switchWriter <span class="token comment" spellcheck="true">// of the bufio.Writer, for return to putBufioWriter</span>

    <span class="token comment" spellcheck="true">// handlerHeader is the Header that Handlers get access to,</span>
    <span class="token comment" spellcheck="true">// which may be retained and mutated even after WriteHeader.</span>
    <span class="token comment" spellcheck="true">// handlerHeader is copied into cw.header at WriteHeader</span>
    <span class="token comment" spellcheck="true">// time, and privately mutated thereafter.</span>
    handlerHeader Header
    <span class="token operator">...</span>
    status        <span class="token builtin">int</span>   <span class="token comment" spellcheck="true">// status code passed to WriteHeader</span>
    <span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre>
<p>它当然实现了上面提到的三个方法，具体代码就不放到这里了，感兴趣的可以自己去看。</p>
<h2 id="6-扩展"><a href="#6-扩展" class="headerlink" title="6. 扩展"></a>6. 扩展</h2><p>虽然 <code>net/http</code> 提供的各种功能已经满足基本需求了，但是很多时候还不够方便，比如：</p>
<ul>
<li>不支持 URL 匹配，所有的路径必须完全匹配，不能捕获 URL 中的变量，不够灵活</li>
<li>不支持 HTTP 方法匹配</li>
<li>不支持扩展和嵌套，URL 处理都在都一个 <code>ServeMux</code> 变量中</li>
</ul>
<p>虽然这些都可以自己手动去码，但实在很不方便。这部分看看有哪些三方的包，都提供了哪些额外的功能。</p>
<h3 id="alice"><a href="#alice" class="headerlink" title="alice"></a><a href="https://github.com/justinas/alice" target="_blank" rel="noopener">alice</a></h3><p>alice 的功能很简单——把多个 handler 串联起来，有请求过来的时候，逐个通过这个 handler 进行处理。</p>
<pre class=" language-go"><code class="language-go">alice<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span>Middleware1<span class="token punctuation">,</span> Middleware2<span class="token punctuation">,</span> Middleware3<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Then</span><span class="token punctuation">(</span>App<span class="token punctuation">)</span>
</code></pre>
<h3 id="Gorilla-Mux"><a href="#Gorilla-Mux" class="headerlink" title="Gorilla Mux"></a><a href="http://www.gorillatoolkit.org/pkg/mux" target="_blank" rel="noopener">Gorilla Mux</a></h3><p>Gorilla 提供了很多网络有关的组件， Mux 就是其中一个，负责 HTTP 的路由功能。这个组件弥补了上面提到的 <code>ServeMux</code> 的一些缺陷，支持的功能有：</p>
<ul>
<li>更多的匹配类型：HTTP 方法、query 字段、URL host 等</li>
<li>支持正则表达式作为 URL path 的一部分，也支持变量提取功能</li>
<li>支持子路由，也就是路由的嵌套，<code>SubRouter</code> 可以实现路由信息的传递</li>
<li>并且和 <code>ServeMux</code> 完全兼容</li>
</ul>
<pre class=" language-go"><code class="language-go">r <span class="token operator">:=</span> mux<span class="token punctuation">.</span><span class="token function">NewRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
r<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/products/{key}"</span><span class="token punctuation">,</span> ProductHandler<span class="token punctuation">)</span>
r<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/articles/{category}/"</span><span class="token punctuation">,</span> ArticlesCategoryHandler<span class="token punctuation">)</span>
r<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/articles/{category}/{id:[0-9]+}"</span><span class="token punctuation">,</span> ArticleHandler<span class="token punctuation">)</span>
</code></pre>
<h3 id="httprouter"><a href="#httprouter" class="headerlink" title="httprouter"></a><a href="https://github.com/julienschmidt/httprouter" target="_blank" rel="noopener">httprouter</a></h3><p>httprouter 和 <code>mux</code> 一样，也是扩展了自带 <code>ServeMux</code> 功能的路由库。它的主要特点是速度快、内存使用少、可扩展性高（使用 radix tree 数据结构进行路由匹配，路由项很多的时候速度也很快）。</p>
<pre class=" language-go"><code class="language-go"><span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">"fmt"</span>
    <span class="token string">"github.com/julienschmidt/httprouter"</span>
    <span class="token string">"net/http"</span>
    <span class="token string">"log"</span>
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">Index</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">,</span> <span class="token boolean">_</span> httprouter<span class="token punctuation">.</span>Params<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    fmt<span class="token punctuation">.</span><span class="token function">Fprint</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"Welcome!\n"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">Hello</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">,</span> ps httprouter<span class="token punctuation">.</span>Params<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    fmt<span class="token punctuation">.</span><span class="token function">Fprintf</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> <span class="token string">"hello, %s!\n"</span><span class="token punctuation">,</span> ps<span class="token punctuation">.</span><span class="token function">ByName</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    router <span class="token operator">:=</span> httprouter<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    router<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> Index<span class="token punctuation">)</span>
    router<span class="token punctuation">.</span><span class="token function">GET</span><span class="token punctuation">(</span><span class="token string">"/hello/:name"</span><span class="token punctuation">,</span> Hello<span class="token punctuation">)</span>

    log<span class="token punctuation">.</span><span class="token function">Fatal</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":8080"</span><span class="token punctuation">,</span> router<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<h3 id="negroni"><a href="#negroni" class="headerlink" title="negroni"></a><a href="https://github.com/urfave/negroni" target="_blank" rel="noopener">negroni</a></h3><p>http middleware 库，支持嵌套的中间件，能够和其他路由库兼容。同时它也自带了不少 middleware 可以使用，比如<code>Recovery</code>、<code>Logger</code>、<code>Static</code>。</p>
<pre class=" language-go"><code class="language-go">router <span class="token operator">:=</span> mux<span class="token punctuation">.</span><span class="token function">NewRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
router<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> HomeHandler<span class="token punctuation">)</span>

n <span class="token operator">:=</span> negroni<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span>Middleware1<span class="token punctuation">,</span> Middleware2<span class="token punctuation">)</span>
<span class="token comment" spellcheck="true">// Or use a middleware with the Use() function</span>
n<span class="token punctuation">.</span><span class="token function">Use</span><span class="token punctuation">(</span>Middleware3<span class="token punctuation">)</span>
<span class="token comment" spellcheck="true">// router goes last</span>
n<span class="token punctuation">.</span><span class="token function">UseHandler</span><span class="token punctuation">(</span>router<span class="token punctuation">)</span>

http<span class="token punctuation">.</span><span class="token function">ListenAndServe</span><span class="token punctuation">(</span><span class="token string">":3001"</span><span class="token punctuation">,</span> n<span class="token punctuation">)</span>
</code></pre>
<h2 id="7-参考"><a href="#7-参考" class="headerlink" title="7. 参考"></a>7. 参考</h2><p>这篇文章参考了以下资料：</p>
<ul>
<li><a href="https://golang.org/pkg/net/http/" target="_blank" rel="noopener">golang net/http 官方文档</a></li>
<li><a href="https://golang.org/src/net/http/server.go" target="_blank" rel="noopener">net/http 源码</a></li>
<li><a href="http://www.alexedwards.net/blog/a-recap-of-request-handling" target="_blank" rel="noopener">A Recap of Request Handling in Go</a></li>
<li><a href="http://soryy.com/blog/2014/not-another-go-net-http-tutorial/" target="_blank" rel="noopener">Not Another Go/Golang net/http Tutorial</a></li>
<li><a href="https://medium.com/@matryer/the-http-handlerfunc-wrapper-technique-in-golang-c60bf76e6124#.esdqmzzah" target="_blank" rel="noopener">the http handlerfunc wrapper technique in golang</a></li>
<li><a href="https://husobee.github.io/golang/url-router/2015/06/15/why-do-all-golang-url-routers-suck.html" target="_blank" rel="noopener">why do all golang url routers suck</a></li>
</ul>

                </div>
            </section>
        </article>
    </div>
    
<nav class="pagination">
    
    
    <a class="prev-post" title="kubernetes 简介： kubelet 和 pod" href="/2016/10/25/kubernetes-intro-kubelet/">
        ← kubernetes 简介： kubelet 和 pod
    </a>
    
    <span class="prev-next-post">•</span>
    
    <a class="next-post" title="etcd 使用入门" href="/2016/08/02/intro-to-etcd/">
        etcd 使用入门 →
    </a>
    
    
</nav>

    <div class="inner">
    <!-- Begin Mailchimp Signup Form -->
    <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
    <style type="text/css">
    	#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
    	/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
    	   We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
    </style>
    <div id="mc_embed_signup">
    <form action="https://cizixs.us7.list-manage.com/subscribe/post?u=2d561b8dea52d73a2e05e6dcb&amp;id=5c710f135b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
        <div id="mc_embed_signup_scroll">
    	<h2>订阅本博客，第一时间收到文章更新</h2>
    <div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
    <div class="mc-field-group">
    	<label for="mce-EMAIL">邮件地址  <span class="asterisk">*</span>
    </label>
    	<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
    </div>
    	<div id="mce-responses" class="clear">
    		<div class="response" id="mce-error-response" style="display:none"></div>
    		<div class="response" id="mce-success-response" style="display:none"></div>
    	</div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2d561b8dea52d73a2e05e6dcb_5c710f135b" tabindex="-1" value=""></div>
        <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
        </div>
    </form>
    </div>
    <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
    <!--End mc_embed_signup-->
    </div>

    <div class="inner">
        <div id="disqus_thread"></div>
    </div>

    
</main>

<div class="t-g-control">
    <div class="gotop">
        <svg class="icon" width="32px" height="32px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M793.024 710.272a32 32 0 1 0 45.952-44.544l-310.304-320a32 32 0 0 0-46.4 0.48l-297.696 320a32 32 0 0 0 46.848 43.584l274.752-295.328 286.848 295.808z" fill="#8a8a8a" /></svg>
    </div>
    <div class="toc-control">
        <svg class="icon toc-icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M779.776 480h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M779.776 672h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M256 288a32 32 0 1 0 0 64 32 32 0 0 0 0-64M392.576 352h387.2a32 32 0 0 0 0-64h-387.2a32 32 0 0 0 0 64M256 480a32 32 0 1 0 0 64 32 32 0 0 0 0-64M256 672a32 32 0 1 0 0 64 32 32 0 0 0 0-64" fill="#8a8a8a" /></svg>
        <svg class="icon toc-close" style="display: none;" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#8a8a8a" /><path d="M557.05545 513.376159l138.367639-136.864185c12.576374-12.416396 12.672705-32.671738 0.25631-45.248112s-32.704421-12.672705-45.248112-0.25631l-138.560301 137.024163-136.447897-136.864185c-12.512727-12.512727-32.735385-12.576374-45.248112-0.063647-12.512727 12.480043-12.54369 32.735385-0.063647 45.248112l136.255235 136.671523-137.376804 135.904314c-12.576374 12.447359-12.672705 32.671738-0.25631 45.248112 6.271845 6.335493 14.496116 9.504099 22.751351 9.504099 8.12794 0 16.25588-3.103239 22.496761-9.247789l137.567746-136.064292 138.687596 139.136568c6.240882 6.271845 14.432469 9.407768 22.65674 9.407768 8.191587 0 16.352211-3.135923 22.591372-9.34412 12.512727-12.480043 12.54369-32.704421 0.063647-45.248112L557.05545 513.376159z" fill="#8a8a8a" /></svg>
    </div>
    <div class="gobottom">
        <svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M231.424 346.208a32 32 0 0 0-46.848 43.584l297.696 320a32 32 0 0 0 46.4 0.48l310.304-320a32 32 0 1 0-45.952-44.544l-286.848 295.808-274.752-295.36z" fill="#8a8a8a" /></svg>
    </div>
</div>
<div class="toc-main" style="right: -100%">
    <div class="post-toc">
        <span>TOC</span>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-初识"><span class="toc-text">1. 初识</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-封装"><span class="toc-text">2. 封装</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-默认"><span class="toc-text">3. 默认</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-路由"><span class="toc-text">4. 路由</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-深入"><span class="toc-text">5. 深入</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Server"><span class="toc-text">Server</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ServeMux"><span class="toc-text">ServeMux</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Request"><span class="toc-text">Request</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ResponseWriter"><span class="toc-text">ResponseWriter</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-扩展"><span class="toc-text">6. 扩展</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#alice"><span class="toc-text">alice</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Gorilla-Mux"><span class="toc-text">Gorilla Mux</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#httprouter"><span class="toc-text">httprouter</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#negroni"><span class="toc-text">negroni</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-参考"><span class="toc-text">7. 参考</span></a></li></ol>
    </div>
</div>



        

<aside class="read-next outer">
    <div class="inner">
        <div class="read-next-feed">
            
            

<article class="read-next-card"  style="background-image: url(https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxcn9ft3j318w0txdo6.jpg)"  >
  <header class="read-next-card-header">
    <small class="read-next-card-header-sitetitle">&mdash; Cizixs Write Here &mdash;</small>
    <h3 class="read-next-card-header-title">Recent Posts</h3>
  </header>
  <div class="read-next-divider">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"/>
    </svg>
  </div>
  <div class="read-next-card-content">
    <ul>
      
      
      
      <li>
        <a href="/2018/08/26/what-is-istio/">什么是 istio</a>
      </li>
      
      
      
      <li>
        <a href="/2018/08/25/knative-serverless-platform/">serverless 平台 knative 简介</a>
      </li>
      
      
      
      <li>
        <a href="/2018/06/25/kubernetes-resource-management/">kubernetes 资源管理概述</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/24/use-prometheus-and-grafana-to-monitor-linux-machine/">使用 promethues 和 grafana 监控自己的 linux 机器</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/13/linux-udp-packet-drop-debug/">linux 系统 UDP 丢包问题分析思路</a>
      </li>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </ul>
  </div>
  <footer class="read-next-card-footer">
    <a href="/archives">  MORE  → </a>
  </footer>
</article>


            
            
            
        </div>
    </div>
</aside>


<footer class="site-footer outer">

	<div class="site-footer-content inner">
		<section class="copyright">
			<a href="/" title="Cizixs Write Here">Cizixs Write Here</a>
			&copy; 2019
		</section>
		<nav class="site-footer-nav">
			
            <a href="https://hexo.io" title="Hexo" target="_blank" rel="noopener">Hexo</a>
            <a href="https://github.com/xzhih/hexo-theme-casper" title="Casper" target="_blank" rel="noopener">Casper</a>
        </nav>
    </div>
</footer>






<div class="floating-header" >
	<div class="floating-header-logo">
        <a href="/" title="Cizixs Write Here">
			
                <img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg" alt="Cizixs Write Here icon" />
			
            <span>Cizixs Write Here</span>
        </a>
    </div>
    <span class="floating-header-divider">&mdash;</span>
    <div class="floating-header-title">go http 服务器编程</div>
    <progress class="progress" value="0">
        <div class="progress-container">
            <span class="progress-bar"></span>
        </div>
    </progress>
</div>
<script>
   $(document).ready(function () {
    var progressBar = document.querySelector('progress');
    var header = document.querySelector('.floating-header');
    var title = document.querySelector('.post-full-title');
    var lastScrollY = window.scrollY;
    var lastWindowHeight = window.innerHeight;
    var lastDocumentHeight = $(document).height();
    var ticking = false;

    function onScroll() {
        lastScrollY = window.scrollY;
        requestTick();
    }
    function requestTick() {
        if (!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }
    function update() {
        var rect = title.getBoundingClientRect();
        var trigger = rect.top + window.scrollY;
        var triggerOffset = title.offsetHeight + 35;
        var progressMax = lastDocumentHeight - lastWindowHeight;
            // show/hide floating header
            if (lastScrollY >= trigger + triggerOffset) {
                header.classList.add('floating-active');
            } else {
                header.classList.remove('floating-active');
            }
            progressBar.setAttribute('max', progressMax);
            progressBar.setAttribute('value', lastScrollY);
            ticking = false;
        }

        window.addEventListener('scroll', onScroll, {passive: true});
        update();

        // TOC
        var width = $('.toc-main').width();
        $('.toc-control').click(function () {
            if ($('.t-g-control').css('width')=="50px") {
                if ($('.t-g-control').css('right')=="0px") {
                    $('.t-g-control').animate({right: width}, "slow");
                    $('.toc-main').animate({right: 0}, "slow");
                    toc_icon()
                } else {
                    $('.t-g-control').animate({right: 0}, "slow");
                    $('.toc-main').animate({right: -width}, "slow");
                    toc_icon()
                }
            } else {
                if ($('.toc-main').css('right')=="0px") {
                    $('.toc-main').slideToggle("fast", toc_icon());
                } else {
                    $('.toc-main').css('right', '0px');
                    toc_icon()
                }
            }
        })

        function toc_icon() {
            if ($('.toc-icon').css('display')=="none") {
                $('.toc-close').hide();
                $('.toc-icon').show();
            } else {
                $('.toc-icon').hide();
                $('.toc-close').show();
            }
        }

        $('.gotop').click(function(){
            $('html,body').animate({scrollTop:$('.post-full-header').offset().top}, 800);
        });
        $('.gobottom').click(function () {
            $('html,body').animate({scrollTop:$('.pagination').offset().top}, 800);
        });

        // highlight
        // https://highlightjs.org
        $('pre code').each(function(i, block) {
            hljs.highlightBlock(block);
        });
        $('td.code').each(function(i, block) {
            hljs.highlightBlock(block);
        });

        console.log("this theme is from https://github.com/xzhih/hexo-theme-casper")
    });
</script>



<link rel="stylesheet" href="https://cdn.staticfile.org/lightgallery/1.3.9/css/lightgallery.min.css">



<script src="https://cdn.staticfile.org/lightgallery/1.3.9/js/lightgallery.min.js"></script>


<script>
	$(function () {
		var postImg = $('#lightgallery').find('img');
		postImg.addClass('post-img');
		postImg.each(function () {
			var imgSrc = $(this).attr('src');
			$(this).attr('data-src', imgSrc);
		});
		$('#lightgallery').lightGallery({selector: '.post-img'});
	});
</script>



<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/

var disqus_config = function () {
this.page.url = 'http://cizixs.com/2016/08/17/golang-http-server-side/';  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'http://cizixs.com/2016/08/17/golang-http-server-side/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};

(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cizixs.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                            


    </div>
</body>
</html>
